/*
 * Copyright 2011-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.sabayframework.mem;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;

/**
 * BigBuffer is a variation of {@link java.nio.Buffer} with long arguments.
 * Interface BigBuffer supports buffers bigger than 2 Gb. 
 * All multibyte operations use native byte order.
 * @author Alex Antonau
 * */
public interface BigBuffer extends Comparable<BigBuffer> {

	/**
	 * Returns this buffer's capacity.
	 * @return  The capacity of this buffer
	 */
	long capacity();

	/**
	 * Returns this buffer's position.
	 * @return  The position of this buffer
	 */
	long position();

	/**
	 * Sets this buffer's position. 
	 *
	 * @param  newPosition
	 *         The new position value; must be non-negative
	 *         and no larger than the current limit
	 *
	 * @return  This buffer
	 *
	 * @throws  IllegalArgumentException
	 *          If the preconditions on <tt>newPosition</tt> do not hold
	 */
	BigBuffer position(long newPosition);

	/**
	 * Returns this buffer's limit.
	 *
	 * @return  The limit of this buffer
	 */
	long limit();

	/**
	 * Sets this buffer's limit.  If the position is larger than the new limit
	 * then it is set to the new limit.  
	 *
	 * @param  newLimit
	 *         The new limit value; must be non-negative
	 *         and no larger than this buffer's capacity
	 *
	 * @return  This buffer
	 *
	 * @throws  IllegalArgumentException
	 *          If the preconditions on <tt>newLimit</tt> do not hold
	 */
	BigBuffer limit(long newLimit);

	/**
	 * Clears this buffer.  The position is set to zero and the limit is set to
	 * the capacity.
	 *
	 * <p> Invoke this method before using a sequence of channel-read or
	 * <i>put</i> operations to fill this buffer.  For example:
	 *
	 * <blockquote><pre>
	 * buf.clear();     // Prepare buffer for reading
	 * in.read(buf);    // Read data</pre></blockquote>
	 *
	 * <p> This method does not actually erase the data in the buffer, but it
	 * is named as if it did because it will most often be used in situations
	 * in which that might as well be the case.
	 *
	 * @return  This buffer
	 */
	BigBuffer clear();

	/**
	 * Flips this buffer.  The limit is set to the current position and then
	 * the position is set to zero.
	 *
	 * <p> After a sequence of channel-read or <i>put</i> operations, invoke
	 * this method to prepare for a sequence of channel-write or relative
	 * <i>get</i> operations.  For example:
	 *
	 * <blockquote><pre>
	 * buf.put(magic);    // Prepend header
	 * in.read(buf);      // Read data into rest of buffer
	 * buf.flip();        // Flip buffer
	 * out.write(buf);    // Write header + data to channel</pre></blockquote>
	 *
	 * <p> This method is often used in conjunction with the {@link
	 * java.nio.ByteBuffer#compact compact} method when transferring data from
	 * one place to another. 
	 *
	 * @return  This buffer
	 */
	BigBuffer flip();

	/**
	 * Rewinds this buffer.  The position is set to zero.
	 *
	 * <p> Invoke this method before a sequence of channel-write or <i>get</i>
	 * operations, assuming that the limit has already been set
	 * appropriately.  For example:
	 *
	 * <blockquote><pre>
	 * out.write(buf);    // Write remaining data
	 * buf.rewind();      // Rewind buffer
	 * buf.get(array);    // Copy data into array</pre></blockquote>
	 *
	 * @return  This buffer
	 */
	BigBuffer rewind();

	/**
	 * Returns the number of elements between the current position and the
	 * limit.
	 *
	 * @return  The number of elements remaining in this buffer
	 */
	long remaining();

	/**
	 * Tells whether there are any elements between the current position and
	 * the limit.
	 *
	 * @return  <tt>true</tt> if, and only if, there is at least one element
	 *          remaining in this buffer
	 */
	boolean hasRemaining();

	/**
	 * Tells whether or not this buffer is read-only.
	 *
	 * @return  <tt>true</tt> if, and only if, this buffer is read-only
	 */
	boolean isReadOnly();

	/**
	 * Tells whether or not this buffer is backed by an accessible
	 * array.
	 *
	 * <p> If this method returns <tt>true</tt> then the {@link #array() array}
	 * and {@link #arrayOffset() arrayOffset} methods may safely be invoked.
	 *
	 *
	 * @return  <tt>true</tt> if, and only if, this buffer
	 *          is backed by an array and is not read-only
	 */
	boolean hasArray();

	/**
	 * Returns the array that backs this
	 * buffer&nbsp;&nbsp;<i>(optional operation)</i>.
	 *
	 * <p> This method is intended to allow array-backed buffers to be
	 * passed to native code more efficiently. Concrete subclasses
	 * provide more strongly-typed return values for this method.
	 *
	 * <p> Modifications to this buffer's content will cause the returned
	 * array's content to be modified, and vice versa.
	 *
	 * <p> Invoke the {@link #hasArray hasArray} method before invoking this
	 * method in order to ensure that this buffer has an accessible backing
	 * array. 
	 *
	 * @return  The array that backs this buffer
	 *
	 * @throws  ReadOnlyBufferException
	 *          If this buffer is backed by an array but is read-only
	 *
	 * @throws  UnsupportedOperationException
	 *          If this buffer is not backed by an accessible array
	 *
	 */
	byte[] array();

	/**
	 * Returns the offset within this buffer's backing array of the first
	 * element of the buffer&nbsp;&nbsp;<i>(optional operation)</i>.
	 *
	 * <p> If this buffer is backed by an array then buffer position <i>p</i>
	 * corresponds to array index <i>p</i>&nbsp;+&nbsp;<tt>arrayOffset()</tt>.
	 *
	 * <p> Invoke the {@link #hasArray hasArray} method before invoking this
	 * method in order to ensure that this buffer has an accessible backing
	 * array. 
	 *
	 * @return  The offset within this buffer's array
	 *          of the first element of the buffer
	 *
	 * @throws  ReadOnlyBufferException
	 *          If this buffer is backed by an array but is read-only
	 *
	 * @throws  UnsupportedOperationException
	 *          If this buffer is not backed by an accessible array
	 *
	 */
	int arrayOffset();

	/**
	 * Tells whether or not this buffer is direct.
	 * <a href="ByteBuffer.html#direct"><i>direct</i></a>.
	 *
	 * @return  <tt>true</tt> if, and only if, this buffer is direct
	 */
	boolean isDirect();

	/**
	 * Retrieves this buffer's byte order.
	 * */
	 ByteOrder order();
	
	/**
	 * Creates a new buffer whose content is a shared subsequence of this buffer's content.<p/>
	 * The content of the new buffer will start at this buffer's current position. Changes to 
	 * this buffer's content will be visible in the new buffer, and vice versa; the two 
	 * buffers' position and limit values will be independent.<p/>
	 *
	 * The new buffer's position will be zero, its capacity and its limit will be the number of bytes 
	 * remaining in this buffer. The new buffer will be direct if, and only 
	 * if, this buffer is direct, and it will be read-only if, and only if, this buffer is read-only.	 
	 * */
	BigBuffer slice();
	
	 /**
     * Creates a new big buffer that shares this buffer's content.
     *
     * <p> The content of the new buffer will be that of this buffer.  Changes
     * to this buffer's content will be visible in the new buffer, and vice
     * versa; the two buffers' position and limit values will be
     * independent.
     *
     * <p> The new buffer's capacity, limit, and position values will be
     * identical to those of this buffer.  The new buffer will be direct if,
     * and only if, this buffer is direct, and it will be read-only if, and
     * only if, this buffer is read-only.  </p>
     *
     * @return  The new byte buffer
     */
    BigBuffer duplicate();

    /**
     * Creates a new, read-only byte buffer that shares this buffer's
     * content.
     *
     * <p> The content of the new buffer will be that of this buffer.  Changes
     * to this buffer's content will be visible in the new buffer; the new
     * buffer itself, however, will be read-only and will not allow the shared
     * content to be modified.  The two buffers' position, and limit
     * values will be independent.
     *
     * <p> The new buffer's capacity, limit, and position values will be
     * identical to those of this buffer.
     *
     * <p> If this buffer is itself read-only then this method behaves in
     * exactly the same way as the {@link #duplicate duplicate} method.  </p>
     *
     * @return  The new, read-only byte buffer
     */
    BigBuffer asReadOnlyBuffer();
	
    /**
     * Compacts this buffer.
     *
     * <p> The bytes between the buffer's current position and its limit,
     * if any, are copied to the beginning of the buffer.  That is, the
     * byte at index <i>p</i>&nbsp;=&nbsp;<tt>position()</tt> is copied
     * to index zero, the byte at index <i>p</i>&nbsp;+&nbsp;1 is copied
     * to index one, and so forth until the byte at index
     * <tt>limit()</tt>&nbsp;-&nbsp;1 is copied to index
     * <i>n</i>&nbsp;=&nbsp;<tt>limit()</tt>&nbsp;-&nbsp;<tt>1</tt>&nbsp;-&nbsp;<i>p</i>.
     * The buffer's position is then set to <i>n+1</i> and its limit is set to
     * its capacity. 
     *
     * <p> The buffer's position is set to the number of bytes copied,
     * rather than to zero, so that an invocation of this method can be
     * followed immediately by an invocation of another relative <i>put</i>
     * method. </p>
     *
     * @return  This buffer
     *
     * @throws  ReadOnlyBufferException
     *          If this buffer is read-only
     */
    BigBuffer compact();
    
    //---------------- absolute accessors
    /**
     * Absolute get method. Reads the byte at the given index.  
     * @param index The byte index from which the value will be read  
     */
    byte get(long index);

    /**
     * Writes the given byte into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The byte value to be written
     * */
    void put(long index, byte value);

    /**
     * Absolute get method. Reads the char at the given index.  
     * @param index The byte index from which the value will be read  
     */
    char getChar(long index);
    
    /**
     * Writes the given char into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The char value to be written
     * */
    void putChar(long index, char value);
    /**
     * Absolute get method. Reads the short at the given index.    
     * @param index The byte index from which the value will be read  
     */
    short getShort(long index);
    /**
     * Writes the given short into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The short value to be written
     * */
    void putShort(long index, short value);

    /**
     * Absolute get method. Reads the int at the given index.    
     * @param index The byte index from which the value will be read  
     */
    int getInt(long index);
    /**
     * Writes the given int into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The int value to be written
     * */
    void putInt(long index, int value);

    /**
     * Absolute get method. Reads the float at the given index.    
     * @param index The byte index from which the value will be read  
     */
    float getFloat(long index);
    /**
     * Writes the given float into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The float value to be written
     * */
    void putFloat(long index, float value);
    
    /**
     * Absolute get method. Reads the long at the given index.    
     * @param index The byte index from which the value will be read  
     */
    long getLong(long index);
    /**
     * Writes the given long into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The long value to be written
     * */
    void putLong(long index, long value);
    
    /**
     * Absolute get method. Reads the double at the given index.    
     * @param index The byte index from which the value will be read  
     */
    double getDouble(long index);
    /**
     * Writes the given double into this buffer at the given index.
     * @param index  The byte index at which the value will be written
     * @param value  The double value to be written
     * */
    void putDouble(long index, double value);
    
    //------------- relative accessors ------------
	/**
	 * Relative get method. Reads the byte at this buffer's current position, 
	 * and then increments the position.
	 * */
	byte get();
		
	/**
	 * Writes the given byte into this buffer at the current position, and then increments the position.
	 * */
	void put(byte b);
	
	/**
	 * Returns the 2-byte short value at this buffer's current position, and then increments the 
	 * position by two.
	 * */
	short getShort();

	/**
	 * Sets the 2-byte short value at the current position, and then increments the position by two.
	 * */
	void putShort(short value);
	
	/**
	 * Returns the 4-byte int value at this buffer's current position, and then increments the 
	 * position by four.
	 * */
	int getInt();
	
	/**
	 * Sets the 4-byte int value at the current position, and then increments the position by four.
	 * */
	void putInt(int value);
	
	/**
	 * Returns the 8-byte long value at this buffer's current position, and then increments the 
	 * position by eight.
	 * */
	long getLong();
	
	/**
	 * Sets the 8-byte long value at the current position, and then increments the position by eight.
	 * */
	void putLong(long value);
	
	/**
	 * Returns the 4-byte float value at this buffer's current position, and then increments the 
	 * position by four.
	 * */
	float getFloat();
	
	/**
	 * Sets the 4-byte float value at the current position, and then increments the position by four.
	 * */
	void putFloat(float value);
	
	/**
	 * Returns the 8-byte double value at this buffer's current position, and then increments the 
	 * position by eight.
	 * */
	double getDouble();

	/**
	 * Sets the 8-byte double value at the current position, and then increments the position by eight.
	 * */
	void putDouble(double value);
	
	/**
	 * Returns the 2-byte char value at this buffer's current position, and then increments the 
	 * position by two.
	 * */
	char getChar();

	/**
	 * Sets the 2-byte char value at the current position, and then increments the position by two.
	 * */
	void putChar(char value);
		
	//---------- byte

	/**
	 * This method transfers bytes from this buffer into the given destination array and then increments the 
	 * position by the length of the array.
	 * */
	void get(byte[] dst);
	
	/**
	 * This method transfers bytes from this buffer into the given destination array and then increments the 
	 * position by given length.
	 * @param dst The array into which bytes are to be written
	 * @param offset The offset within the array of the first byte to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of bytes to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(byte[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source byte array into this buffer and then increments the 
	 * position by the length of the array.
	 * */
	void put(byte[] src);
	/**
	 * This method transfers bytes into this buffer from the given source array and then increments the 
	 * position by given length.
	 * @param src The array from which bytes are to be read
	 * @param offset The offset within the array of the first byte to be read; must be non-negative and no larger than array.length
	 * @param length The number of bytes to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(byte[] src, int offset, int length);
	
	//---------- short
	/**
	 * This method transfers shorts from this buffer into the given destination array and then increments the 
	 * position by 2 * length of the array.
	 * */
	void get(short[] dst);
	
	/**
	 * This method transfers shorts from this buffer into the given destination array and then increments the 
	 * position by 2 *  length.
	 * @param dst The array from which shorts are to be read
	 * @param offset The offset within the array of the first short to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of shorts to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(short[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source short array into this buffer and then increments the 
	 * position by 2 * length of the array.
	 * */
	void put(short[] src);
	/**
	 * This method transfers shorts into this buffer from the given source array and then increments the 
	 * position by 2 * length.
	 * @param src The array into which bytes are to be written
	 * @param offset The offset within the array of the first short to be read; must be non-negative and no larger than array.length
	 * @param length The number of shorts to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(short[] src, int offset, int length);
	
	//---------------- char
	/**
	 * This method transfers chars from this buffer into the given destination array and then increments the 
	 * position by 2 * length of the array.
	 * */
	void get(char[] dst);
	
	/**
	 * This method transfers chars from this buffer into the given destination array and then increments the 
	 * position by 2 *  length.
	 * @param dst The array into which chars are to be written
	 * @param offset The offset within the array of the first char to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of chars to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(char[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source char array into this buffer and then increments the 
	 * position by 2 * length of the array.
	 * */
	void put(char[] src);
	/**
	 * This method transfers chars into this buffer from the given source array and then increments the 
	 * position by 2 * length.
	 * @param src The array from which chars are to be read
	 * @param offset The offset within the array of the first char to be read; must be non-negative and no larger than array.length
	 * @param length The number of chars to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(char[] src, int offset, int length);

	//---------------- int
	/**
	 * This method transfers ints from this buffer into the given destination array and then increments the 
	 * position by 4 * length of the array.
	 * */
	void get(int[] dst);
	
	/**
	 * This method transfers ints from this buffer into the given destination array and then increments the 
	 * position by 4 * length.
	 * @param dst The array into which ints are to be written
	 * @param offset The offset within the array of the first int to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of ints to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(int[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source int array into this buffer and then increments the 
	 * position by 4 * length of the array.
	 * */
	void put(int[] src);
	/**
	 * This method transfers ints into this buffer from the given source array and then increments the 
	 * position by 4 * length.
	 * @param src The array from which ints are to be read
	 * @param offset The offset within the array of the first int to be read; must be non-negative and no larger than array.length
	 * @param length The number of ints to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(int[] src, int offset, int length);
	
	//---------------- float
	/**
	 * This method transfers floats from this buffer into the given destination array and then increments the 
	 * position by 4 * length of the array.
	 * */
	void get(float[] dst);
	
	/**
	 * This method transfers floats from this buffer into the given destination array and then increments the 
	 * position by 4 * length.
	 * @param dst The array into which floats are to be written
	 * @param offset The offset within the array of the first float to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of floats to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(float[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source float array into this buffer and then increments the 
	 * position by 4 * length of the array.
	 * */
	void put(float[] src);
	/**
	 * This method transfers floats into this buffer from the given source array and then increments the 
	 * position by 4 * length.
	 * @param src The array from which floats are to be read
	 * @param offset The offset within the array of the first float to be read; must be non-negative and no larger than array.length
	 * @param length The number of floats to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(float[] src, int offset, int length);
	
	//---------------- long
	/**
	 * This method transfers longs from this buffer into the given destination array and then increments the 
	 * position by 8 * length of the array.
	 * */
	void get(long[] dst);
	
	/**
	 * This method transfers longs from this buffer into the given destination array and then increments the 
	 * position by 8 * length.
	 * @param dst The array into which longs are to be written
	 * @param offset The offset within the array of the first long to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of longs to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(long[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source long array into this buffer and then increments the 
	 * position by 8 * length of the array.
	 * */
	void put(long[] src);
	/**
	 * This method transfers longs into this buffer from the given source array and then increments the 
	 * position by 8 * length.
	 * @param src The array from which longs are to be read
	 * @param offset The offset within the array of the first long to be read; must be non-negative and no larger than array.length
	 * @param length The number of longs to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(long[] src, int offset, int length);
	
	//---------------- double
	/**
	 * This method transfers doubles from this buffer into the given destination array and then increments the 
	 * position by 8 * length of the array.
	 * */
	void get(double[] dst);
	
	/**
	 * This method transfers doubles from this buffer into the given destination array and then increments the 
	 * position by 8 * length.
	 * @param dst The array into which doubles are to be written
	 * @param offset The offset within the array of the first double to be written; must be non-negative and no larger than dst.length
	 * @param length The maximum number of doubles to be written to the given array; must be non-negative and no larger than dst.length - offset
	 * */
	void get(double[] dst, int offset, int length);

	/**
	 * This method transfers the entire content of the given source double array into this buffer and then increments the 
	 * position by 8 * length of the array.
	 * */
	void put(double[] src);
	/**
	 * This method transfers doubles into this buffer from the given source array and then increments the 
	 * position by 8 * length.
	 * @param src The array from which doubles are to be read
	 * @param offset The offset within the array of the first double to be read; must be non-negative and no larger than array.length
	 * @param length The number of doubles to be read from the given array; must be non-negative and no larger than array.length - offset
	 * */
	void put(double[] src, int offset, int length);
	

	/**
	 * This method transfers the bytes remaining in the given source buffer into this buffer.
	 * This method copies n = src.remaining() bytes from the given buffer into this buffer, starting at each buffer's current position. 
	 * The positions of both buffers are then incremented by n.
	 * @param src The source buffer from which bytes are to be read; must not be this buffer
	 * @throws IllegalArgumentException If the source buffer is this buffer
	 * @throws ReadOnlyBufferException  If this buffer is read-only
	 * */
	void put(BigBuffer src);

	/**
	 * This method transfers the bytes remaining in the given source buffer into this buffer.
	 * This method copies n = src.remaining() bytes from the given buffer into this buffer, starting at each buffer's current position. 
	 * The positions of both buffers are then incremented by n.
	 * @param src The source buffer from which bytes are to be read; must not be this buffer
	 * @throws IllegalArgumentException If the source buffer is this buffer
	 * @throws ReadOnlyBufferException  If this buffer is read-only
	 * */
	void put(ByteBuffer src);
	
	/**
	 * Free, if possible, resources allocated for this buffer.
	 * After calling this method this buffer should not be used. 
	 * */
	void destroy();
	
}